﻿Public Class ColorManager

    Const COLOR_COUNT As Integer = 10
    Const LUM_COUNT As Integer = 5
    Const MIN_LUMINANCE As Double = 0.7
    Const MAX_LUMINANCE As Double = 0.9
    Const DIFF_LUMINANCE As Double = MAX_LUMINANCE - MIN_LUMINANCE

    Private Shared _groupHueList As New Dictionary(Of Integer, Double)
    Private Shared _itemLuminanceList As New Dictionary(Of Integer, Dictionary(Of Integer, Double))

    Private Sub New()
        ' singleton
    End Sub

    Public Shared Function GetColor(ByVal groupKey As Integer, ByVal itemKey As Integer) As Color
        Dim hue As Double = GetGroupHue(groupKey)
        Dim luminance As Double = GetItemLuminance(groupKey, itemKey)
        Return GetColorFromHSL(hue, 1, luminance)
    End Function

    Private Shared Function GetGroupHue(ByVal groupKey As Integer) As Double
        If (Not _groupHueList.ContainsKey(groupKey)) Then
            Dim hue As Double = (_groupHueList.Count Mod COLOR_COUNT) / COLOR_COUNT
            _groupHueList.Add(groupKey, hue)
        End If
        Return _groupHueList(groupKey)
    End Function

    Private Shared Function GetItemLuminance(ByVal groupKey As Integer, ByVal itemKey As Integer) As Double
        If (Not _itemLuminanceList.ContainsKey(groupKey)) Then
            _itemLuminanceList.Add(groupKey, New Dictionary(Of Integer, Double))
        End If
        Dim groupItemLuminanceList As Dictionary(Of Integer, Double) = _itemLuminanceList(groupKey)
        If (Not groupItemLuminanceList.ContainsKey(itemKey)) Then
            Dim itemLuminance As Double = (groupItemLuminanceList.Count Mod LUM_COUNT) * DIFF_LUMINANCE / LUM_COUNT + MIN_LUMINANCE
            groupItemLuminanceList.Add(itemKey, itemLuminance)
        End If
        Return groupItemLuminanceList(itemKey)
    End Function

    Private Shared Function GetColorFromHSL(ByVal h As Double, ByVal s As Double, ByVal l As Double) As Color
        Dim r As Double = 0
        Dim g As Double = 0
        Dim b As Double = 0
        If (l > 0) Then
            If (s = 0) Then
                r = l
                g = l
                b = l
            Else
                Dim temp2 As Double = GetTemp2(s, l)
                Dim temp1 As Double = 2.0 * l - temp2

                r = GetColorComponent(temp1, temp2, h + 1.0 / 3.0)
                g = GetColorComponent(temp1, temp2, h)
                b = GetColorComponent(temp1, temp2, h - 1.0 / 3.0)
            End If
        End If
        Return Color.FromArgb(Convert.ToInt32(255 * r), Convert.ToInt32(255 * g), Convert.ToInt32(255 * b))
    End Function

    Private Shared Function GetTemp2(ByVal s As Double, ByVal l As Double) As Double
        Dim t2 As Double
        If (l < 0.5) Then
            t2 = l * (1.0 + s)
        Else
            t2 = l + s - (l * s)
        End If
        Return t2
    End Function

    Private Shared Function GetColorComponent(ByVal t1 As Double, ByVal t2 As Double, ByVal t3 As Double) As Double
        t3 = MoveIntoRange(t3)
        If (t3 < 1.0 / 6.0) Then
            Return t1 + (t2 - t1) * 6.0 * t3
        ElseIf (t3 < 0.5) Then
            Return t2
        ElseIf (t3 < 2.0 / 3.0) Then
            Return t1 + ((t2 - t1) * ((2.0 / 3.0) - t3) * 6.0)
        Else
            Return t1
        End If
    End Function

    Private Shared Function MoveIntoRange(ByVal t3 As Double) As Double
        If (t3 < 0.0) Then
            t3 += 1.0
        ElseIf (t3 > 1.0) Then
            t3 -= 1.0
        End If
        Return t3
    End Function

End Class
